type TSetFactory<T> = new (eles?: any[]) => T;

export abstract class SetBase<T extends SetBase<T, K>, K> {
  protected items: K;
  private SetFactory: TSetFactory<T>;
  
  public constructor(SetFactory: TSetFactory<T>, itemsInitail: K, eles: any[] = []) {
    this.items = itemsInitail;
    this.SetFactory = SetFactory;
    for (let val of eles) {
      this.add(val);
    }
  }

  public getUnion(otherSet: T): T {
    const union = new this.SetFactory();
    
    this.values().forEach(ele => union.add(ele));
    otherSet.values().forEach(ele => union.add(ele));
    
    return union;
  }

  public getInsertsection(otherSet: T): T {
    const insertsection = new this.SetFactory();
    let biggerSet = this.values();
    let smallerSet = otherSet.values();

    biggerSet.forEach(ele => {
      if (smallerSet.includes(ele)) {
        insertsection.add(ele);
      }
    });
    return insertsection;
  }

  public getDifference(otherSet: T): T {
    const difference = new this.SetFactory();

    this.values().forEach(ele => {
      if (!otherSet.has(ele)) {
        difference.add(ele);
      }
    });

    return difference;
  }

  public isSubSetOf(otherSet: T): boolean {
    const subTestValues = this.values();
    let isSubSet = true;

    if (subTestValues.length > otherSet.size()) {
      isSubSet = false;
    } else {
      isSubSet = !subTestValues.some(ele => !otherSet.has(ele));
    }

    return isSubSet;
  }

  public abstract add(ele): boolean;

  public abstract delete(ele: any): boolean;

  public abstract has(ele: any): boolean;

  public abstract clear():void;

  public abstract size():number;
  
  public abstract values(): any[];
}